all repos — caroster @ f5c854ad35aa58a77481d22c3554680f8828f45f

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Linkify from 'linkify-react';
  3import Tooltip from '@mui/material/Tooltip';
  4import IconButton from '@mui/material/IconButton';
  5import Box from '@mui/material/Box';
  6import Link from '@mui/material/Link';
  7import Card from '@mui/material/Card';
  8import Container from '@mui/material/Container';
  9import TextField from '@mui/material/TextField';
 10import Typography from '@mui/material/Typography';
 11import TuneIcon from '@mui/icons-material/Tune';
 12import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 13import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
 14import {useTheme} from '@mui/material/styles';
 15import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 16import {PropsWithChildren, useState} from 'react';
 17import {useTranslation} from 'next-i18next';
 18import pageUtils from '../../../lib/pageUtils';
 19import DetailsLink from '../../../containers/DetailsLink';
 20import ShareEvent from '../../../containers/ShareEvent';
 21import PlaceInput from '../../../containers/PlaceInput';
 22import LangSelector from '../../../components/LangSelector';
 23import usePermissions from '../../../hooks/usePermissions';
 24import useEventStore from '../../../stores/useEventStore';
 25import useToastStore from '../../../stores/useToastStore';
 26import EventLayout, {TabComponent} from '../../../layouts/Event';
 27import {
 28  EventByUuidDocument,
 29  useUpdateEventMutation,
 30} from '../../../generated/graphql';
 31import {langLocales} from '../../../locales/constants';
 32import {getLocaleForLang} from '../../../lib/getLocale';
 33
 34interface Props {
 35  eventUUID: string;
 36  announcement?: string;
 37}
 38
 39const Page = (props: PropsWithChildren<Props>) => {
 40  return <EventLayout {...props} Tab={DetailsTab} />;
 41};
 42
 43const DetailsTab: TabComponent<Props> = ({}) => {
 44  const {t} = useTranslation();
 45  const {
 46    userPermissions: {canEditEventDetails},
 47  } = usePermissions();
 48  const theme = useTheme();
 49  const [updateEvent] = useUpdateEventMutation();
 50  const addToast = useToastStore(s => s.addToast);
 51  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 52  const event = useEventStore(s => s.event);
 53  const [isEditing, setIsEditing] = useState(false);
 54
 55  if (!event) return null;
 56
 57  const hasGeoloc = event.latitude && event.longitude;
 58
 59  const onSave = async e => {
 60    try {
 61      const {uuid, ...data} = event;
 62      delete data.linkedEvent;
 63      delete data.isReturnEvent;
 64      const {
 65        id,
 66        travels,
 67        waitingPassengers,
 68        __typename,
 69        administrators,
 70        passengers,
 71        ...input
 72      } = data;
 73      await updateEvent({
 74        variables: {
 75          uuid,
 76          eventUpdate: {
 77            ...input,
 78          },
 79        },
 80        refetchQueries: ['eventByUUID'],
 81      });
 82      setIsEditing(false);
 83    } catch (error) {
 84      console.error(error);
 85      addToast(t('event.errors.cant_update'));
 86    }
 87  };
 88
 89  const modifyButton = isEditing ? (
 90    <Tooltip
 91      title={t('event.details.save')}
 92      sx={{
 93        position: 'absolute',
 94        top: theme.spacing(2),
 95        right: theme.spacing(2),
 96      }}
 97    >
 98      <IconButton color="primary" onClick={onSave}>
 99        <CheckCircleOutlineIcon />
100      </IconButton>
101    </Tooltip>
102  ) : (
103    <Tooltip
104      title={t('event.details.modify')}
105      sx={{
106        position: 'absolute',
107        top: theme.spacing(2),
108        right: theme.spacing(2),
109      }}
110    >
111      <IconButton color="primary" onClick={() => setIsEditing(true)}>
112        <TuneIcon />
113      </IconButton>
114    </Tooltip>
115  );
116
117  return (
118    <Box
119      sx={{
120        position: 'relative',
121      }}
122    >
123      <Container
124        sx={{
125          p: 4,
126          mt: 6,
127          mb: 11,
128          mx: 0,
129          [theme.breakpoints.down('md')]: {
130            p: 2,
131          },
132        }}
133      >
134        <Card
135          sx={{
136            position: 'relative',
137            maxWidth: '100%',
138            width: '480px',
139            p: 2,
140          }}
141        >
142          <Typography variant="h4" pb={2}>
143            {t('event.details')}
144          </Typography>
145          {canEditEventDetails() && modifyButton}
146          {(isEditing || event.name) && (
147            <Box pt={2} pr={1.5}>
148              <Typography variant="overline">
149                {t('event.fields.name')}
150              </Typography>
151              <Typography>
152                {isEditing ? (
153                  <TextField
154                    size="small"
155                    fullWidth
156                    value={event.name}
157                    onChange={e => setEventUpdate({name: e.target.value})}
158                    name="name"
159                    id="EditEventName"
160                  />
161                ) : (
162                  <Typography id="EventName">{event.name}</Typography>
163                )}
164              </Typography>
165            </Box>
166          )}
167          {(isEditing || event.date) && (
168            <Box pt={2} pr={1.5}>
169              <Typography variant="overline">
170                {t('event.fields.date')}
171              </Typography>
172              {isEditing ? (
173                <Typography>
174                  <DatePicker
175                    slotProps={{
176                      textField: {
177                        size: 'small',
178                        id: `EditEventDate`,
179                        fullWidth: true,
180                        placeholder: t('event.fields.date_placeholder'),
181                      },
182                    }}
183                    format="DD/MM/YYYY"
184                    value={moment(event.date)}
185                    onChange={date =>
186                      setEventUpdate({
187                        date: !date ? null : moment(date).format('YYYY-MM-DD'),
188                      })
189                    }
190                  />
191                </Typography>
192              ) : (
193                <Box position="relative">
194                  <Typography id="EventDate">
195                    {moment(event.date).format('DD/MM/YYYY')}
196                  </Typography>
197                </Box>
198              )}
199            </Box>
200          )}
201          {(isEditing || event.address) && (
202            <Box pt={2} pr={1.5}>
203              <Typography variant="overline">
204                {t('event.fields.address')}
205              </Typography>
206              {isEditing ? (
207                <PlaceInput
208                  place={event.address}
209                  latitude={event.latitude}
210                  longitude={event.longitude}
211                  onSelect={({place, latitude, longitude}) =>
212                    setEventUpdate({
213                      address: place,
214                      latitude,
215                      longitude,
216                    })
217                  }
218                />
219              ) : (
220                <Box position="relative">
221                  <Typography
222                    id="EventAddress"
223                    title={t`placeInput.noCoordinates`}
224                    sx={{
225                      pr: 3,
226                      display: 'inline-flex',
227                      alignItems: 'center',
228                      columnGap: 1,
229                    }}
230                  >
231                    <Link
232                      target="_blank"
233                      rel="noreferrer"
234                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
235                        event.address
236                      )}`}
237                      onClick={e => e.preventDefault}
238                    >
239                      {event.address}
240                    </Link>
241                    {!hasGeoloc && (
242                      <InfoOutlinedIcon fontSize="small" color="warning" />
243                    )}
244                  </Typography>
245                </Box>
246              )}
247            </Box>
248          )}
249          {(isEditing || event.description) && (
250            <Box pt={2} pr={1.5}>
251              <Typography variant="overline">
252                {t('event.fields.description')}
253              </Typography>
254              {isEditing ? (
255                <Typography>
256                  <TextField
257                    fullWidth
258                    multiline
259                    maxRows={4}
260                    inputProps={{maxLength: 250}}
261                    value={event.description || ''}
262                    onChange={e =>
263                      setEventUpdate({description: e.target.value})
264                    }
265                    id={`EditEventDescription`}
266                    name="description"
267                  />
268                </Typography>
269              ) : (
270                <Typography
271                  id="EventDescription"
272                  sx={{pr: 3, whiteSpace: 'pre-line'}}
273                >
274                  <Linkify options={{render: DetailsLink}}>
275                    {event.description}
276                  </Linkify>
277                </Typography>
278              )}
279            </Box>
280          )}
281          {(isEditing || event.lang) && (
282            <Box pt={2} pr={1.5}>
283              <Typography variant="overline">
284                {t('event.fields.lang')}
285              </Typography>
286              {isEditing ? (
287                <LangSelector
288                  value={event.lang}
289                  onChange={lang => setEventUpdate({lang})}
290                />
291              ) : (
292                <Typography id="EventLang" sx={{pr: 3}}>
293                  {langLocales[event.lang]}
294                </Typography>
295              )}
296            </Box>
297          )}
298          {!isEditing && (
299            <ShareEvent
300              title={`Caroster ${event.name}`}
301              sx={{width: '100%', mt: 2}}
302            />
303          )}
304        </Card>
305      </Container>
306    </Box>
307  );
308};
309
310export const getServerSideProps = pageUtils.getServerSideProps(
311  async (context, apolloClient) => {
312    const {uuid} = context.query;
313    const {host = ''} = context.req.headers;
314    let event = null;
315
316    // Fetch event
317    try {
318      const {data} = await apolloClient.query({
319        query: EventByUuidDocument,
320        variables: {uuid},
321      });
322      event = data?.eventByUUID?.data;
323    } catch (error) {
324      return {
325        notFound: true,
326      };
327    }
328
329    const description = await getLocaleForLang(
330      event?.attributes?.lang,
331      'meta.description'
332    );
333
334    return {
335      props: {
336        eventUUID: uuid,
337        metas: {
338          title: event?.attributes?.name || '',
339          description,
340          url: `https://${host}${context.resolvedUrl}`,
341        },
342      },
343    };
344  }
345);
346export default Page;